/*
TinyXmlStd http://code.google.com/p/tinyxmlstd/
based on TinyXml, www.sourceforge.net/projects/tinyxml
Original code (2.0 and earlier )copyright (c) 2000-2006 Lee Thomason (www.grinninglizard.com)

This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any
damages arising from the use of this software.

Permission is granted to anyone to use this software for any
purpose, including commercial applications, and to alter it and
redistribute it freely, subject to the following restrictions:

1. The origin of this software must not be misrepresented; you must
not claim that you wrote the original software. If you use this
software in a product, an acknowledgment in the product documentation
would be appreciated but is not required.

2. Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original software.

3. This notice may not be removed or altered from any source
distribution.
*/

#include <sstream>
#include <fstream>
#include <iostream>
#include <iomanip>

#include "tinyxmlstd.h"

namespace tinyxmlstd
{
	// can be found in tinyxmlparser.cpp
	const Char* Document_parse(Document* doc, const Char* pointer, ParsingData* prevData, Encoding::Type encoding, WhitespaceAction::Type whitespaceAction, ErrorStore* es);

	InFileReader::InFileReader(const String& source)
		: mData(source)
	{
	}

	InFileReader::InFileReader(const Ostringstream& s)
		: mData(s.str())
	{
	}

	InFileReader::InFileReader(Istream& f)
	{
		//s.rdbuf(f.rdbuf());
		Ostringstream s;
		for(String line; std::getline(f, line);)
			s << line << std::endl;
		mData = s.str();
	}

	InFile InFileReader::data() const
	{
		return InFile(mData.c_str(), mData.length());
	}

	InFileReader::operator InFile() const
	{
		return data();
	}

	OutFile::~OutFile()
	{
	}

	OutFileStream::OutFileStream(Ostream& o)
		: os(o)
	{
	}
	void OutFileStream::print(const String& str)
	{
		os << str;
	}
	void OutFileStream::print(const Char c)
	{
		os << c;
	}

	InFile::InFile(const Char* data, std::size_t length)
		: mData(data)
		, mLength(length)
	{
	}

	const std::size_t InFile::length() const
	{
		return mLength;
	}

	const Char* const InFile::getDataPointer() const
	{
		return mData;
	}

	// casting
	ElementPtr Element::Cast(NodePtr node)
	{
		return boost::dynamic_pointer_cast<Element>(node);
	}
	DeclarationPtr Declaration::Cast(NodePtr node)
	{
		return boost::dynamic_pointer_cast<Declaration>(node);
	}
	UnknownPtr Unknown::Cast(NodePtr node)
	{
		return boost::dynamic_pointer_cast<Unknown>(node);
	}
	CommentPtr Comment::Cast(NodePtr node)
	{
		return boost::dynamic_pointer_cast<Comment>(node);
	}
	TextPtr Text::Cast(NodePtr node)
	{
		return boost::dynamic_pointer_cast<Text>(node);
	}
	DocumentPtr Document::Cast(NodePtr node)
	{
		return boost::dynamic_pointer_cast<Document>(node);
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


	struct StringStreamer
	{
	public:
		template<typename T>
		StringStreamer& operator << (const T& t)
		{
			ss << t;
			return *this;
		}
		operator String() const
		{
			return ss.str();
		}

		Ostringstream ss;
	};

	FileLocation::FileLocation()
		: row(-1)
		, character(-1)
	{
	}

	FileLocation::FileLocation(int r, int c)
		: row(r)
		, character(c)
	{
	}

	void FileLocation::clear()
	{
		row = character = -1;
	}

	bool FileLocation::hasRow() const
	{
		return row != -1;
	}

	bool FileLocation::hasCharacter() const
	{
		return character != -1;
	}

	bool FileLocation::hasBothRowAndCharacter() const
	{
		return hasRow() && hasCharacter();
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


	Visitor::~Visitor()
	{
	}

	VisitorAction::Type Visitor::visitEnter(const Document&) { return VisitorAction::Continue; }

	VisitorAction::Type Visitor::visitExit(const Document&) { return VisitorAction::Continue; }


	VisitorAction::Type Visitor::visitEnter(const Element&) { return VisitorAction::Continue; }

	VisitorAction::Type Visitor::visitExit(const Element&) { return VisitorAction::Continue; }


	VisitorAction::Type Visitor::visit(const Declaration&) { return VisitorAction::Continue; }

	VisitorAction::Type Visitor::visit(const Text&) { return VisitorAction::Continue; }

	VisitorAction::Type Visitor::visit(const Comment&) { return VisitorAction::Continue; }

	VisitorAction::Type Visitor::visit(const Unknown&) { return VisitorAction::Continue; }

	VisitorAction::Type Visitor::doAccept(Node& node)
	{
		return node.accept(this);
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	Base::Base()
		: mUserData(0)
	{
	}

	void Base::setFileLocation(const FileLocation& fl)
	{
		mLocation = fl;
	}

	void Base::setUserData( void* user ) { mUserData = user; } ///< Set a pointer to arbitrary user data.
	void* Base::getUserData() { return mUserData; } ///< Get a pointer to arbitrary user data.
	const void* Base::getUserData() const { return mUserData; } ///< Get a pointer to arbitrary user data.

	bool AppendSpecialChar(const char c, Ostringstream* outString )
	{
		String t;
		if( GetSpecialChar_fromChar(c, &t) )
		{
			*outString << t;
			return true;
		}
		else return false;
	}

	String EncodeString(const String& str)
	{
		std::size_t i=0;
		Ostringstream outString;
		const std::size_t strlength = str.length();

		while(i<strlength )
		{
			unsigned char c = (unsigned char) str[i];

			if ( c == '&' 
				&& i < ( strlength - 2 )
				&& str[i+1] == '#'
				&& str[i+2] == 'x' )
			{
				// Hexadecimal character reference.
				// Pass through unchanged.
				// &#xA9; -- copyright symbol, for example.
				//
				// The -1 is a bug fix from Rob Laveaux. It keeps
				// an overflow from happening if there is no ';'.
				// There are actually 2 ways to exit this loop -
				// while fails (error case) and break (semicolon found).
				// However, there is no mechanism (currently) for
				// this function to return an error.
				while ( i<strlength-1 )
				{
					outString << str[i];
					++i;
					if ( str[i] == ';' )
						break;
				}
			}
			else if ( AppendSpecialChar(c, &outString) )
			{
				++i;
			}
			else if ( c < 32 )
			{
				// Easy pass at non-alpha/numeric/symbol
				// Below 32 is symbolic.
				outString << _T("&#x") << std::setw(2) << std::setfill(_T('0')) << std::right // 2 characters minimum, fills with 0, aligns to the right
					<< std::hex << std::uppercase << (c & 0xff);
				//sprintf( buf, "&#x%02X;", (unsigned) ( c & 0xff ) );
				++i;
			}
			else
			{
				//char realc = (char) c;
				//outString->append( &realc, 1 );
				outString << (char) c; // somewhat more efficient function call.
				++i;
			}
		}

		return outString.str();
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	NodePtr GetFirstChild(const ChildList& node)
	{
		if( node.empty() ) return NodePtr();
		else return node.list()[0];
	}

	TextPtr GetFirstText(const ChildList& node)
	{
		return Text::Cast(GetFirstChild(node));
	}

	ElementPtr GetFirstChildElement(const ChildList& node)
	{
		const NodeList& nl = node.list();
		for(std::size_t i=0; i<nl.size(); ++i)
		{
			if( ElementPtr el = Element::Cast(nl[i]) ) return el;
		}
		return ElementPtr();
	}

	ElementPtr GetFirstChildElementNamed(const String& name, const ChildList& node)
	{
		const NodeList& nl = node.list();
		for(std::size_t i=0; i<nl.size(); ++i)
		{
			if( ElementPtr el = Element::Cast(nl[i]) )
			{
				if( el->getName() == name ) return el;
			}
		}
		return ElementPtr();
	}

	Node* Node::getParent()
	{
		return mParent;
	}

	void Node::setParent(Node* parent)
	{
		mParent = parent;
	}

	const ChildList& Node::getChildren() const
	{
		return mNodes;
	}

	ChildList& Node::getChildren()
	{
		return mNodes;
	}

	const Node* Node::getParent() const { return mParent; }

	NodeType::Type Node::getType() const { return mType; }

	Node::Node( NodeType::Type type )
		: Base()
		, mParent(0)
		, mType(type)
	{
		mNodes.internal_postConstructor(this);
	}


	Node::~Node()
	{
	}

	void ChildList::clear()
	{
		mNodes.clear();
	}

	ChildList::ChildList()
		: mParent(0)
	{
	}

	const NodeList& ChildList::list() const
	{
		return mNodes;
	}

	void ChildList::internal_postConstructor(Node* parent)
	{
		XmlAssert(mParent == 0 ); // dont call this function yourself, this is done automagically
		mParent = parent;
	}

	bool ChildList::empty() const
	{
		return mNodes.empty();
	}

	void ChildList::add(NodePtr node )
	{
		XmlAssert(mParent); // internal error, postConstructor should have been called
		XmlAssert( node->getParent() == 0 ); // should not have a parent...
		//XmlAssert( node->getDocument() == 0 || node->getDocument() == this->getDocument() );
		XmlAssert( node->getType() != NodeType::Document );

		node->setParent(mParent);

		mNodes.push_back(node);
	}

	void ChildList::accept(Visitor* visitor) const
	{
		for(std::size_t i=0;i<mNodes.size(); ++i)
		{
			NodePtr node = mNodes[i];
			if ( VisitorAction::Abort == node->accept( visitor ) )
				return;
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	ElementsIterator::ElementsIterator()
		: list(0)
	{
	}

	ElementsIterator::ElementsIterator(ChildList* l, NodeList::iterator i)
		: list(l)
		, iter(i)
	{
		if( iter == list->list().end() ) return;
		if( Element::Cast(*iter) ) return; // is element
		else increment(); // else get the next element
	}

	void ElementsIterator::increment()
	{
		while(iter != list->list().end() )
		{
			++iter;
			if( iter != list->list().end() && Element::Cast(*iter) )
			{
				// is element...
				return;
			}
		}
	}

	bool ElementsIterator::equal(ElementsIterator const& other) const
	{
		return list == other.list && iter == other.iter;
	}

	Element& ElementsIterator::dereference() const
	{
		return *Element::Cast(*iter);
	}

	ElementsIterator ChildList::elements_begin()
	{
		return ElementsIterator(this, mNodes.begin());
	}

	ElementsIterator ChildList::elements_end()
	{
		return ElementsIterator(this, mNodes.end());
	}

	std::pair<ElementsIterator, ElementsIterator> ElementsIn(ChildList& list)
	{
		return std::make_pair(list.elements_begin(), list.elements_end() );
	}

	///////////////////////////////////////////

	ElementsNamedIterator::ElementsNamedIterator()
		: list(0)
	{
	}

	ElementsNamedIterator::ElementsNamedIterator(ChildList* l, NodeList::iterator i, const String& n)
		: list(l)
		, iter(i)
		, name(n)
	{
		if( iter == list->list().end() ) return;
		ElementPtr e = Element::Cast(*iter);
		if( e && e->getName()==name ) return; // is element with right name
		else increment(); // else get the next element
	}
			
	void ElementsNamedIterator::increment()
	{
		while(iter != list->list().end() )
		{
			++iter;
			if( iter != list->list().end() )
			{
				 ElementPtr e = Element::Cast(*iter);
				 if(e && e->getName() == name)
				 {
					// is element with the right name...
					return;
				 }
			}
		}
	}

	bool ElementsNamedIterator::equal(ElementsNamedIterator const& other) const
	{
		return list == other.list && name == other.name && iter == other.iter;
	}

	Element& ElementsNamedIterator::dereference() const
	{
		return *Element::Cast(*iter);
	}

	ElementsNamedIterator ChildList::elementsNamed_begin(const String& name)
	{
		return ElementsNamedIterator(this, mNodes.begin(), name);
	}

	ElementsNamedIterator ChildList::elementsNamed_end(const String& name)
	{
		return ElementsNamedIterator(this, mNodes.end(), name);
	}

	std::pair<ElementsNamedIterator, ElementsNamedIterator> ElementsNamedIn(const String& name, ChildList& list)
	{
		return std::make_pair(list.elementsNamed_begin(name), list.elementsNamed_end(name));
	}
	
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	const String& Element::getName() const
	{
		return mName;
	}

	void Element::setName(const String& name)
	{
		mName = name;
	}

	AttributeSet& Element::getAttributes()
	{
		return mAttributeSet;
	}

	const AttributeSet& Element::getAttributes() const
	{
		return mAttributeSet;
	}

	Element::Element (const String& name)
		: Node(NodeType::Element)
		, mName(name)
	{
	}

	Element::~Element()
	{
	}

	VisitorAction::Type Element::accept( Visitor* visitor ) const
	{
		if ( VisitorAction::Continue == visitor->visitEnter(*this) )
		{
			getChildren().accept(visitor);
		}
		return visitor->visitExit( *this );
	}

	/*const String& Element::getChildText() const
	{
		const NodePtr child = GetFirstChild(this);
		if ( child.get() == 0 ) throw "no children";
		const Text* childText = child->asText();
		if ( childText == 0) throw "child is not a text";
		return childText->getValue();
	}*/

	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	const String kErrorString[ Error::NumberOfErrorCodes ] =
	{
		_T("No error"),
		_T("Error"),
		_T("Failed to open file"),
		_T("Error parsing Element."),
		_T("Failed to read Element name"),
		_T("Error reading Element value."),
		_T("Error reading Attributes."),
		_T("Error: empty tag."),
		_T("Error reading end tag."),
		_T("Error parsing Unknown."),
		_T("Error parsing Comment."),
		_T("Error parsing Declaration."),
		_T("Error document empty."),
		_T("Error null (0) or unexpected EOF found in input stream."),
		_T("Error parsing CDATA."),
		_T("Error when Document added to document, because Document can only be at the root."),
	};

	bool ErrorStore::hasError() const
	{
		return mErrorId != Error::None;
	}

	const String& ErrorStore::getErrorDescription() const
	{
		return kErrorString[ mErrorId ];
	}

	Error::Type ErrorStore::getErrorId() const
	{
		return mErrorId;
	}

	int ErrorStore::getErrorRow() const
	{
		return mErrorLocation.row+1;
	}

	int ErrorStore::getErrorChar() const
	{
		return mErrorLocation.character+1;
	}

	ErrorStore::ErrorStore()
	{
	}

	void ErrorStore::clearError()
	{
		mErrorId = Error::None;
		mErrorLocation.row = mErrorLocation.character = 0;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	bool LoadFile(Document* doc, const String& file)
	{
		Ifstream f(file.c_str());
		if( f.good() == false ) return false;
		InFileReader ifr(f);
		return doc->load(ifr);
	}

	const ErrorStore& Document::getError() const
	{
		return mError;
	}

	bool Document::calculateFileLocation() const
	{
		return mCalculateFileLocation;
	}

	void Document::calculateFileLocation(bool cfl)
	{
		mCalculateFileLocation = cfl;
	}

	Document::~Document() {}

	void Document::setMicrosoftBom(bool bom)
	{
		mUseMicrosoftBOM = bom;
	}

	bool Document::getMicrosoftBom() const
	{
		return mUseMicrosoftBOM;
	}

	const ElementPtr Document::getRootElement() const
	{
		return GetFirstChildElement( getChildren() );
	}

	ElementPtr Document::getRootElement()
	{
		return GetFirstChildElement( getChildren() );
	}

	void Document::setTabSize( int tabsize ) { mTabsize = tabsize; }

	int Document::getTabSize() const { return mTabsize; }

	Document::Document()
		: Node( NodeType::Document )
		, mTabsize(4)
		, mUseMicrosoftBOM(false)
		, mCalculateFileLocation(true)
	{
	}


	bool Document::load( const String& source, Encoding::Type encoding )
	{
		// There was a really terrifying little bug here. The code:
		// value = filename
		// in the STL case, cause the assignment method of the String to
		// be called. What is strange, is that the String had the same
		// address as it's c_str() method, and so bad things happen. Looks
		// like a bug in the Microsoft STL implementation.
		// Add an extra string to avoid the crash.
		InFileReader file( source );
			//setError( Error::OpeningFile, 0, 0, Encoding::Unknown );

		InFile infile = file.data();
		load( file.data(), encoding );
		return true;
	}

	bool Document::load(const InFile& file, Encoding::Type encoding )
	{
		// Delete the existing data:
		getChildren().clear();
		setFileLocation(FileLocation());

		// Get the file size, so we can pre-allocate the string. HUGE speed impact.
		const std::size_t length = file.length();

		// Strange case, but good to handle up front.
		if ( length <= 0 )
		{
			mError.setError( Error::DocumentEmpty, 0, 0, Encoding::Unknown );
			return false;
		}

		// If we have a file, assume it is all one big XML file, and read it in.
		// The document parser may decide the document ends sooner than the entire file, however.
		String data;
		data.reserve( length );

		// Subtle bug here. TinyXml did use fgets. But from the XML spec:
		// 2.11 End-of-Line Handling
		// <snip>
		// <quote>
		// ...the XML processor MUST behave as if it normalized all line breaks in external 
		// parsed entities (including the document entity) on input, before parsing, by translating 
		// both the two-character sequence #xD #xA and any #xD that is not followed by #xA to 
		// a single #xA character.
		// </quote>
		//
		// It is not clear fgets does that, and certainly isn't clear it works cross platform. 
		// Generally, you expect fgets to translate from the convention of the OS to the c/unix
		// convention, and not work generally.

		/*
		while( fgets( buf, sizeof(buf), file ) )
		{
		data += buf;
		}
		*/

		const Char* const buf = file.getDataPointer();

		const Char* lastPos = buf;
		const Char* pointer = buf;

		while( *pointer ) {
			XmlAssert( pointer < (buf+length) );
			if ( *pointer == 0xa ) {
				// Newline character. No special rules for this. Append all the characters
				// since the last string, and include the newline.
				data.append( lastPos, (pointer-lastPos+1) ); // append, include the newline
				++pointer; // move past the newline
				lastPos = pointer; // and point to the new buffer (may be 0)
				XmlAssert( pointer <= (buf+length) );
			}
			else if ( *pointer == 0xd ) {
				// Carriage return. Append what we have so far, then
				// handle moving forward in the buffer.
				if ( (pointer-lastPos) > 0 ) {
					data.append( lastPos, pointer-lastPos ); // do not add the CR
				}
				data += (char)0xa; // a proper newline

				if ( *(pointer+1) == 0xa ) {
					// Carriage return - new line sequence
					pointer += 2;
					lastPos = pointer;
					XmlAssert( pointer <= (buf+length) );
				}
				else {
					// it was followed by something else...that is presumably characters again.
					++pointer;
					lastPos = pointer;
					XmlAssert( pointer <= (buf+length) );
				}
			}
			else {
				++pointer;
			}
		}
		// Handle any left over characters.
		if ( pointer-lastPos ) {
			data.append( lastPos, pointer-lastPos );
		}

		Document_parse(this, data.c_str(), 0, encoding, WhitespaceAction::Condense, &mError);

		return mError.hasError() == false;
	}


	void Document::save( OutFile& fp ) const
	{
		if ( mUseMicrosoftBOM )
		{
			const unsigned char TIXML_UTF_LEAD_0 = 0xefU;
			const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;
			const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;

			fp.print(TIXML_UTF_LEAD_0);
			fp.print(TIXML_UTF_LEAD_1);
			fp.print(TIXML_UTF_LEAD_2);
		}
		Printer printer(fp);
		accept(&printer);
	}

	VisitorAction::Type Document::accept( Visitor* visitor ) const
	{
		if ( VisitorAction::Continue == visitor->visitEnter(*this) )
		{
			getChildren().accept(visitor);
		}
		return visitor->visitExit( *this );
	}


	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	Attribute::Attribute() : Base()
	{
	}

	Attribute::Attribute( const String& name, const String& value )
	{
		mName = name;
		mValue = value;
	}

	const String& Attribute::getName() const { return mName; } ///< Return the name of this attribute.
	const String& Attribute::getValue() const { return mValue; } ///< Return the value of this attribute.

	void Attribute::setName( const String& name ) { mName = name; } ///< Set the name of this attribute.
	void Attribute::setValue( const String& value ) { mValue = value; } ///< Set the value.

	bool Attribute::operator==( const Attribute& rhs ) const { return rhs.mName == mName; }
	bool Attribute::operator<( const Attribute& rhs ) const { return mName < rhs.mName; }
	bool Attribute::operator>( const Attribute& rhs ) const { return mName > rhs.mName; }

	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	Comment::Comment() : Node( NodeType::Comment )
	{
	}

	/// Construct a comment from text.
	Comment::Comment( const String& value )
		: Node( NodeType::Comment )
		, mValue(value)
	{
	}

	Comment::~Comment()
	{
	}

	const String& Comment::getValue() const
	{
		return mValue;
	}

	void Comment::setValue(const String& val)
	{
		mValue = val;
	}

	VisitorAction::Type Comment::accept( Visitor* visitor ) const
	{
		return visitor->visit( *this );
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	bool Text::isCdata() const
	{
		return mIsCdata;
	}

	const String& Text::getValue() const
	{
		return mValue;
	}

	void Text::setValue(const String& val)
	{
		mValue = val;
	}

	void Text::setCdata( bool cdata )
	{
		mIsCdata = cdata;
	}

	Text::~Text()
	{
	}

	Text::Text (const String& initValue )
		: Node (NodeType::Text)
		, mValue(initValue)
	{
		mIsCdata = false;
	}


	VisitorAction::Type Text::accept( Visitor* visitor ) const
	{
		return visitor->visit( *this );
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


	Declaration::Declaration() : Node( NodeType::Declaration ) {}

	Declaration::~Declaration() {}

	const String& Declaration::getVersion() const
	{
		return mVersion;
	}

	const String& Declaration::getEncoding() const
	{
		return mEncoding;
	}

	const String& Declaration::getStandalone() const
	{
		return mStandalone;
	}

	const void Declaration::setVersion(const String& val)
	{
		mVersion = val;
	}

	const void Declaration::setEncoding(const String& val)
	{
		mEncoding = val;
	}

	const void Declaration::setStandalone(const String& val)
	{
		mStandalone = val;
	}

	Declaration::Declaration( const String& version,
							 const String& encoding,
							 const String& standalone )
							 : Node( NodeType::Declaration )
	{
		mVersion = version;
		mEncoding = encoding;
		mStandalone = standalone;
	}

	VisitorAction::Type Declaration::accept( Visitor* visitor ) const
	{
		return visitor->visit( *this );
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////



	Unknown::Unknown() : Node( NodeType::Unknown )
	{
	}

	const String& Unknown::getCode() const
	{
		return mCode;
	}

	void Unknown::setCode(const String& code)
	{
		mCode = code;
	}

	Unknown::~Unknown()
	{
	}

	VisitorAction::Type Unknown::accept( Visitor* visitor ) const
	{
		return visitor->visit( *this );
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	
	void AttributeSet::clear()
	{
		mAttributeMap.clear();
		mAttributeList.clear();
	}

	void AttributeSet::set(const String& name, const String& value)
	{
		if ( AttributePtr result = find( name ) )
		{
			result->setValue( value );
		}
		else
		{
			AttributePtr a(new Attribute( name, value ));
			add( a );
		}
	}

	const String& AttributeSet::get(const String& name) const
	{
		const AttributePtr result = find( name );
		if( result.get() == 0 ) throw "missing attribute";
		return result->getValue();
	}

	AttributeSet::AttributeSet()
	{
	}


	AttributeSet::~AttributeSet()
	{
	}

	void AttributeSet::add( AttributePtr addMe )
	{
		XmlAssert( !find( String( addMe->getName() ) ) ); // Shouldn't be multiply adding to the set.
		mAttributeMap[addMe->getName()] = addMe;
		mAttributeList.push_back(addMe);
	}

	void Remove(NodeList& list, NodePtr node)
	{
		list.erase(list.end(),
				std::remove_if(
					  list.begin()
					, list.end()
					, std::bind1st(std::equal_to<NodePtr>(), node)
				)
			);
	}
	void Remove(AttributeList& list, AttributePtr node)
	{
		list.erase(list.end(),
				std::remove_if(
					  list.begin()
					, list.end()
					, std::bind1st(std::equal_to<AttributePtr>(), node)
				)
			);
	}

	void AttributeSet::remove(const String& name)
	{
		AttributePtr a = find(name);
		XmlAssert(a); // attribute must exist
		mAttributeMap.erase(name);
		Remove(mAttributeList, a);
	}

	AttributePtr AttributeSet::find( const String& name )
	{
		AttributeMap::iterator iter = mAttributeMap.find(name);
		if( iter == mAttributeMap.end() ) return AttributePtr();
		else return iter->second;
	}

	const AttributePtr AttributeSet::find( const String& name ) const
	{
		AttributeMap::const_iterator iter = mAttributeMap.find(name);
		if( iter == mAttributeMap.end() ) return AttributePtr();
		else return iter->second;
	}

	const AttributeMap& AttributeSet::map() const
	{
		return mAttributeMap;
	}

	const AttributeList& AttributeSet::list() const
	{
		return mAttributeList;
	}

	template <typename T>
	QueryResult::Type DoQueryFunction(const AttributeSet& as, const String& name, T* val)
	{
		const AttributePtr a = as.find(name);
		if( a == 0 ) return QueryResult::NoAttribute;
		Istringstream ss(a->getValue());
		ss >> *val;
		if( ss.good() ) return QueryResult::Success;
		else return QueryResult::WrongType;
	}
	template <typename T>
	T DoGetFunction(const AttributeSet& as, const String& name)
	{
		T t;
		if( QueryResult::Success != DoQueryFunction<T>(as, name, &t) )
		{
			throw "missing attribute or wrong type";
		}
		return t;
	}

	template <typename T>
	String ToString(const T& t)
	{
		Ostringstream ss;
		ss << t;
		return ss.str();
	}

	QueryResult::Type AttributeSet::queryIntValue(const String& name, int* ival ) const
	{
		return DoQueryFunction<int>(*this, name, ival);
	}

	QueryResult::Type AttributeSet::queryDoubleValue(const String& name, double* dval ) const
	{
		return DoQueryFunction<double>(*this, name, dval);
	}

	QueryResult::Type AttributeSet::query(const String& name, String* sval ) const
	{
		const AttributePtr a = find(name);
		if( a == 0 ) return QueryResult::NoAttribute;
		*sval = a->getValue();
		return QueryResult::Success;
	}

	void AttributeSet::set(const String& name, int value)
	{
		set(name, ToString<int>(value));
	}

	void AttributeSet::set(const String& name, double value )
	{
		set(name, ToString<double>(value));
	}

	int AttributeSet::getIntValue(const String& name) const
	{
		return DoGetFunction<int>(*this, name);
	}

	double AttributeSet::getDoubleValue(const String& name) const
	{
		return DoGetFunction<double>(*this, name);
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


	Handle::Handle( NodePtr node ) { this->mNode = node; }
	Handle::Handle( const Handle& ref ) { this->mNode = ref.mNode; }
	Handle Handle::operator=( const Handle& ref ) { this->mNode = ref.mNode; return *this; }

	NodePtr Handle::asNode() const { return mNode; }

	ElementPtr Handle::asElement() const
	{
		return Element::Cast(mNode);
	}

	TextPtr Handle::asText() const
	{
		return Text::Cast(mNode);
	}

	UnknownPtr Handle::asUnknown() const
	{
		return Unknown::Cast(mNode);
	}

	Handle Handle::getFirstChild() const
	{
		if ( mNode )
		{
			NodePtr child = GetFirstChild(mNode->getChildren());
			if ( child )
				return Handle( child );
		}
		return Handle( NodePtr() );
	}

	Handle Handle::getFirstChildElement() const
	{
		if ( mNode )
		{
			ElementPtr child = GetFirstChildElement(mNode->getChildren());
			if ( child )
				return Handle( child );
		}
		return Handle( NodePtr() );
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


	Printer::Printer(OutFile& file)
	: mDepth( 0 )
	, mSimpleTextPrint( false )
	, mIndent( _T(" ") )
	, mLineBreak( _T("\n") )
	, mFile(file)
	{
	}

	Printer& Printer::setIndent( const String& indent )
	{
		mIndent = indent;
		return *this;
	}

	const String& Printer::getIndent()
	{
		return mIndent;
	}

	Printer& Printer::setLineBreak( const String& lineBreak )
	{
		mLineBreak = lineBreak;
		return *this;
	}

	const String& Printer::getLineBreak()
	{
		return mLineBreak;
	}

	Printer& Printer::setDenseFormat()
	{
		mIndent.clear();
		mLineBreak.clear();
		return *this;
	}

	void Printer::appendIndent()
	{
		for( int i=0; i<mDepth; ++i )
			mFile.print(mIndent);
	}
	void Printer::appendLineBreak()
	{
		mFile.print(mLineBreak);
	}

	VisitorAction::Type Printer::visitEnter( const Document& )
	{
		return VisitorAction::Continue;
	}

	VisitorAction::Type Printer::visitExit( const Document& )
	{
		return VisitorAction::Continue;
	}

	VisitorAction::Type Printer::visitEnter(const Element& element)
	{
		appendIndent();
		mFile.print(_T("<"));
		mFile.print(element.getName());

		const AttributeSet& attributes = element.getAttributes();
		for( AttributeList::const_iterator attrib = attributes.list().begin(); attrib != attributes.list().end(); ++attrib )
		{
			mFile.print(_T(" "));

			{
				String n, v;

				n = EncodeString(attrib->get()->getName());
				v = EncodeString(attrib->get()->getValue());

				if (attrib->get()->getValue().find ('\"') == String::npos) {
					mFile.print(n + _T("=\"") + v + _T("\""));
				}
				else {
					mFile.print(n + _T("='") + v + _T("'"));
				}
			}
		}

		if ( element.getChildren().empty() )
		{
			mFile.print(_T(" />"));
			appendLineBreak();
		}
		else 
		{
			mFile.print(_T(">"));
			TextPtr text = Text::Cast(element.getChildren().list()[0]);
			if ( text
				&& element.getChildren().list().size() == 1
				&& text->isCdata() == false )
			{
				mSimpleTextPrint = true;
				// no DoLineBreak()!
			}
			else
			{
				appendLineBreak();
			}
		}
		++mDepth;
		return VisitorAction::Continue;
	}


	VisitorAction::Type Printer::visitExit( const Element& element )
	{
		--mDepth;
		if ( element.getChildren().empty() )
		{
			// nothing.
		}
		else 
		{
			if ( mSimpleTextPrint )
			{
				mSimpleTextPrint = false;
			}
			else
			{
				appendIndent();
			}
			mFile.print(_T("</"));
			mFile.print(element.getName());
			mFile.print(_T(">"));
			appendLineBreak();
		}
		return VisitorAction::Continue;
	}


	VisitorAction::Type Printer::visit( const Text& text )
	{
		if ( text.isCdata() )
		{
			appendIndent();
			mFile.print(_T("<![CDATA["));
			mFile.print(text.getValue());
			mFile.print(_T("]]>"));
			appendLineBreak();
		}
		else if ( mSimpleTextPrint )
		{
			String str = EncodeString( text.getValue() );
			mFile.print(str);
		}
		else
		{
			appendIndent();
			String str = EncodeString( text.getValue() );
			mFile.print(str);
			appendLineBreak();
		}
		return VisitorAction::Continue;
	}


	VisitorAction::Type Printer::visit( const Declaration& declaration )
	{
		appendIndent();
		{
			mFile.print(_T("<?xml ") );

			if ( !declaration.getVersion().empty() ) {
				mFile.print(_T("version=\"") + declaration.getVersion() + _T("\" "));
			}
			if ( !declaration.getEncoding().empty() ) {
				mFile.print(_T("encoding=\"") + declaration.getEncoding() + _T("\" "));
			}
			if ( !declaration.getStandalone().empty() ) {
				mFile.print(_T("standalone=\"") + declaration.getStandalone() + _T("\" "));
			}
			mFile.print(_T("?>") );
		}
		appendLineBreak();
		return VisitorAction::Continue;
	}

	VisitorAction::Type Printer::visit( const Comment& comment )
	{
		appendIndent();
		mFile.print(_T("<!--"));
		mFile.print(comment.getValue());
		mFile.print(_T("-->"));
		appendLineBreak();
		return VisitorAction::Continue;
	}

	VisitorAction::Type Printer::visit( const Unknown& unknown )
	{
		appendIndent();
		mFile.print(_T("<"));
		mFile.print(unknown.getCode());
		mFile.print(_T(">"));
		appendLineBreak();
		return VisitorAction::Continue;
	}
}

